Touchpad Interrupt Example
Preface¶
When you see this part of the content, it means that you have already delved quite deeply into Hackintosh, and the touchpad working in polling mode no longer satisfies you. You won't be satisfied until you get a GPIO interrupt. Actually, this content is taken from this article on my blog: Research on Touchpad Drivers for Dual - screen Laptops under macOS
Since the article is rather long, I've extracted the key parts and put them in this tutorial. Interested friends can also go to the original article to read it. Now, let's directly start the part of the touchpad interrupt tutorial.
Hardware Information¶
Before handling the interrupt, first collect the basic information of your touchpad. First, briefly organize the touchpad information under Windows. A standard touchpad that complies with the Microsoft I2C protocol is used:
Under macOS, IORegistryExplorer can also be used to identify that the model of the touchpad is: GDX1515
The above potentially useful information is organized as follows:
- The name of this 2 - in - 1 touchpad under Windows: ScreenPad
- Touchpad model: GDX1515
- Touchpad screen model: ScreenXpert
- IRQ:
0x0000006D (109)
- APIC Pin:
6d(109)
- Device instance path:
ACPI\GDX1515\1
- Hardware Id:
ACPI\VEN_GDX&DEV_1515
ACPI\GDX1515
*GDX1515
- BIOS device name:
\_SB.PCI0.I2C1.ETPD
- Location path:
ACPI(_SB)#ACPI(PCI0)#(I2C1)#ACPI(ETPD)
ACPI(_SB)#ACPI(USBX)#(I2C1)#ACPI(ETPD)
Working Modes of the Touchpad¶
The more perfect situation is to make the touchpad work in interrupt mode (interrupts). For polling and interrupt, you can refer to the following concepts:
- APIC Interrupt
- The interrupt mode used by macOS, with perfect functionality, and only a very few devices support it.
- It is only supported when the APIC Pin value is less than
2F (47)
. - GPIO Interrupt
- The interrupt mode mostly used in the Windows system.
- It is second only to the APIC interrupt and is relatively efficient, but you need to modify and customize the SSDT yourself.
- Polling
- A relatively inefficient mode.
- But it has relatively good compatibility and is applicable to most touchpads.
- It is prone to problems such as pointer drift and other unresponsive BUGs.
So, in what mode is our current touchpad working? From the previous information collection, we know that the APIC Pin value of the touchpad is:
- IRQ:
0x0000006D (109)
- APIC Pin:
6d(109)
Judging the Working Mode of the Touchpad¶
So, how to judge the current working mode of the touchpad? There are currently three methods as follows:
Viewing the dmesg Log¶
Under normal circumstances, using the dmesg command, you can directly search for the working state of interrupt or polling based on the keywords of the touchpad model:
However, Hackintosh is never smooth sailing. As you can see, the log result viewed by dmesg is empty. This is because the macOS 12.0 system restricts the content viewed by dmesg. Here, we need to manually load kexts to view the log.
Turning off SIP¶
Many OC configurations have SIP enabled by default. Since we want to load our own Kexts, we need to manually switch the SIP status on the interface where we choose the system at boot:
If your OC doesn't have this option, it's because this configuration switch isn't checked. For specific details, you can refer to this part of my article: My Hackintosh Installation Tutorial: Teach You to Configure OpenCore Step by Step
Disabling Kexts in OC¶
The kexts injected through OpenCore can't be used to view the dmesg log normally. So, we need to manually disable the relevant kexts:
Manually Loading Kexts¶
Copy VoodooI2C.kext
and VoodooI2CHID.kext
to the desktop, and then execute the following commands:
# Modify the owner
sudo chown -R root:wheel VoodooI2C*
# Modify the permissions
sudo chmod -R 755 VoodooI2C*
# Load kexts
sudo kextload -v VoodooI2C*
Of course, the first time you load kexts, you need to agree to the permission in the "Security & Privacy" setting, and then restart the computer to successfully load them:
After the above operations, the final viewing effect is as follows:
It can be seen that from the dmesg log, this GDX1515 touchpad is working in polling mode.
For the details of log viewing, a separate subsection will be opened below to introduce them in detail.
Using IORegistryExplore¶
The above method may be a bit cumbersome. Actually, using IORegistryExplorer can also directly show the current working state of the touchpad. Because under the device manager in Windows, the location path of my touchpad is:
ACPI(_SB)#ACPI(PCI0)#(I2C1)#ACPI(ETPD)
ACPI(_SB)#ACPI(USBX)#(I2C1)#ACPI(ETPD)
So, searching for ETPD
can show the detailed information of our touchpad. Generally, two results will be searched out. The following is the first result:
However, the first result has no reference value. We usually focus on the second result searched out:
The above picture is a typical situation where it is not working in interrupt mode.
At this time, some netizens will surely ask, if it is in GPIO interrupt mode, what should it look like here?
2333 This is a good question. I consulted Bat.bat guru. The following are the original words of the guru:
When the code reaches GPIO, there will be a set property to write IRQ and Pin to ioreg. So, when using IORegistryExplorer to view, just pay attention to whether these two new properties exist.¶
Speaking of this, some netizens may still not be very clear. Below, I will post a screenshot of IORegistryExplorer in GPIO interrupt mode, and everyone should understand:
Customizing and Patching DSDT¶
Extracting DSDT¶
Since making the touchpad definitely requires customizing the SSDT, extracting the original DSDT of the motherboard is essential.
There are many methods to extract DSDT. You can use Clover. Under Windows, you can use AIDA64. Under macOS, you can use DPCIManager. Just open it and click "Extract DSDT" in the upper - left corner:
DSDT Troubleshooting¶
You can directly load DSDT in ACPI through OC, or you can separately extract the relevant code of the touchpad in DSDT and save it as an SSDT file. Both methods are fine. Here, I directly modify it in DSDT (simple and crude).
For DSDT troubleshooting, the essential MaciASL software is used here. Just click "Compile" to compile. If I'm not wrong, there will definitely be a bunch of errors:
This is because the ACPI specifications of the DSDT of each motherboard are not unified. So, we have to modify the errors according to our own programming experience. It's not easy to describe this in an article because each motherboard is different. My errors here are relatively easy to understand. I just deleted these Zero
codes directly. 23333 (Specific troubleshooting has to be done according to the corresponding error situation)
Finally, it's successfully 0 errors:
If DSDT has no problem, it can be directly loaded using OC:
Finding the Touchpad Code¶
Since from the previous information collection, we know that the path of the touchpad is ETPD
, we can directly search for ETPD to find the touchpad code.
It is found that the path in the lower - left corner also meets the situation of our previous information collection (ACPI(_SB)#ACPI(PCI0)#(I2C1)#ACPI(ETPD)
):
The touchpad code part is pasted below:
Scope (_SB.PCI0.I2C1)
{
Device (ETPD)
{
Name (_ADR, One) // _ADR: Address
Name (ETPH, Package (0x01)
{
"ASUE1407"
})
Name (FTPH, Package (0x09)
{
"FTE1001",
"FTE1200",
"FTE1200",
"FTE1300",
"FTE1300",
"FTE1201",
"FTE1200",
"FTE1200",
"FTE1200"
})
Name (GTPH, Package (0x05)
{
"GDX1505",
"GDX1300",
"GDX1200",
"GDX1301",
"GDX1515"
})
Method (_HID, 0, NotSerialized) // _HID: Hardware ID
{
If (And (TPDI, 0x04))
{
Return (DerefOf (Index (ETPH, TPHI)))
}
If (And (TPDI, 0x10))
{
Return (DerefOf (Index (FTPH, TPHI)))
}
If (And (TPDI, 0x40))
{
Return (DerefOf (Index (GTPH, TPHI)))
}
Return ("ELAN1010")
}
Name (_CID, "PNP0C50") // _CID: Compatible ID
Name (_UID, One) // _UID: Unique ID
Name (_S0W, 0x03) // _S0W: S0 Device Wake State
Method (_DSM, 4, NotSerialized) // _DSM: Device - Specific Method
{
If (LEqual (Arg0, ToUUID ("3cdff6f7-4267-4555-ad05-b30a3d8938de") /* HID I2C Device */))
{
If (LEqual (Arg2, Zero))
{
If (LEqual (Arg1, One))
{
Return (Buffer (One)
{
0x03
})
}
Else
{
Return (Buffer (One)
{
0x00
})
}
}
If (LEqual (Arg2, One))
{
Return (One)
}
}
Else
{
Return (Buffer (One)
{
0x00
})
}
}
Method (_STA, 0, NotSerialized) // _STA: Status
{
If (LOr (LNotEqual (TPIF, One), LAnd (DSYN, One)))
{
Return (Zero)
}
Return (0x0F)
}
Method (_CRS, 0, Serialized) // _CRS: Current Resource Settings
{
Name (SBFI, ResourceTemplate ()
{
I2cSerialBusV2 (0x0015, ControllerInitiated, 0x00061A80,
AddressingMode7Bit, "\\_SB.PCI0.I2C1",
0x00, ResourceConsumer,, Exclusive,
)
Interrupt (ResourceConsumer, Level, ActiveLow, Exclusive,,, )
{
0x0000006D,
}
})
Return (SBFI)
}
}
}
}
GPIO Pinning Fixing¶
Here, I only introduce how to customize the GPIO interrupt. Polling and other modes are not within the scope of this article.
Finding the Key Touchpad Code¶
According to the feature of the _CRS
method, we can easily find a code snippet similar to the following in the touchpad code:
Method (_CRS, 0, Serialized) // _CRS: Current Resource Settings
{
Name (SBFI, ResourceTemplate ()
{
I2cSerialBusV2 (0x0015, ControllerInitiated, 0x00061A80,
AddressingMode7Bit, "\\_SB.PCI0.I2C1",
0x00, ResourceConsumer,, Exclusive,
)
Interrupt (ResourceConsumer, Level, ActiveLow, Exclusive,,, )
{
0x0000006D,
}
})
Return (SBFI)
}
Rename SBFI¶
When VoodooI2C calls the _CRS method of the touch device in the DSDT in GPIO interrupt mode, it always uses the SBFG
parameter instead of the SBFI
parameter. So the SBFI
variable in our current touchpad code doesn't meet the requirements. We first rename SBFI
to SBFB
. As for the SBFG
variable, we will add it separately later (if it doesn't exist in your DSDT).
Method (_CRS, 0, Serialized) // _CRS: Current Resource Settings
{
Name (SBFB, ResourceTemplate ()
{
I2cSerialBusV2 (0x0015, ControllerInitiated, 0x00061A80,
AddressingMode7Bit, "\\_SB.PCI0.I2C1",
0x00, ResourceConsumer,, Exclusive,
)
// Interrupt (ResourceConsumer, Level, ActiveLow, Exclusive,,, )
// {
// 0x0000006D,
// }
})
Return (SBFB)
}
Interrupt (ResourceConsumer, Level, ActiveLow, Exclusive,,, )
{
0x0000006D,
}
Look for GPIO Pin¶
Look for a code snippet similar to the following in the touchpad code:
Name (SBFG, ResourceTemplate ()
{
GpioInt (Level, ActiveLow, ExclusiveAndWake, PullDefault, 0x0000,
"\\_SB.PCI0.GPI0", 0x00, ResourceConsumer,,
)
{ // Pin list
0x0000
}
})
Add GPIO Template¶
Obviously, I can't find the GPIO - related code in my touchpad. So we need to copy the above code snippet and add it under the _CRS method:
Method (_CRS, 0, Serialized) // _CRS: Current Resource Settings
{
Name (SBFB, ResourceTemplate ()
{
I2cSerialBusV2 (0x0015, ControllerInitiated, 0x00061A80,
AddressingMode7Bit, "\\_SB.PCI0.I2C1",
0x00, ResourceConsumer,, Exclusive,
)
})
// The following is the newly added GPIO template code snippet
Name (SBFG, ResourceTemplate ()
{
GpioInt (Level, ActiveLow, ExclusiveAndWake, PullDefault, 0x0000,
"\\_SB.PCI0.GPI0", 0x00, ResourceConsumer,,
)
{ // Pin list
0x0000 // We will calculate this value later
}
})
Return (SBFB)
}
Modify the _CRS Return Value¶
Since we have also introduced the SBFG variable in the _CRS method, we need to change the default Return (SBFB)
return value to:
Return (ConcatenateResTemplate (SBFB, SBFG))
Original words of the big shot Bat.bat: For devices with gpioint, there is no need to calculate the pin. For those without it, adding it usually won't succeed. Calculating the pin is actually the last - ditch solution.
Calculate the GPIO Pin Value¶
In the template we added above, the GPIO Pin value is 0x0000
. Generally, this won't work and can only serve as a placeholder. So we need to calculate a correct GPIO value. This step is crucial. Different CPUs have different calculation formulas. The following formula is also provided by Bat.bat.
- Skylake (intel 6th - generation CPU)
If APICPIN > 47 And APICPIN <= 79 Then
GPIOPIN = APICPIN - 24
GPIOPIN2 = APICPIN + 72
ElseIf APICPIN > 79 And APICPIN <= 119 Then
GPIOPIN = APICPIN - 24
End If
- CoffeeLake - H (intel 8th - generation high - voltage CPU)
If APICPIN > 47 And APICPIN <= 71 Then
GPIOPIN = APICPIN - 16
GPIOPIN2 = APICPIN + 240
If APICPIN > 47 And APICPIN <= 59 Then GPIOPIN3 = APICPIN + 304
ElseIf APICPIN > 71 And APICPIN <= 95 Then
GPIOPIN = APICPIN - 8
GPIOPIN3 = APICPIN + 152
GPIOPIN2 = APICPIN + 120
ElseIf APICPIN > 95 And APICPIN <= 119 Then
GPIOPIN = APICPIN
If APICPIN > 108 And APICPIN <= 115 Then
GPIOPIN2 = APICPIN + 20
End If
- CoffeeLake - LF and Whiskylake (intel 8th - generation low - voltage CPU and Whiskylake - architecture CPU)
If APICPIN > 47 And APICPIN <= 71 Then
GPIOPIN = APICPIN - 16
GPIOPIN2 = APICPIN + 80
ElseIf APICPIN > 71 And APICPIN <= 95 Then
GPIOPIN2 = APICPIN + 184
GPIOPIN = APICPIN + 88
ElseIf APICPIN > 95 And APICPIN <= 119 Then
GPIOPIN = APICPIN
ElseIf APICPIN > 108 And APICPIN <= 115 Then
GPIOPIN2 = APICPIN - 44
End If
The CPU of my laptop is i7 - 10510U
, which is a low - voltage CPU of Comet Lake. We will find that there is no formula for the Comet Lake series above. However, Bat.bat said that the 10th - generation is a clone of the 8th - generation. So we can use the formula of CoffeeLake - LF and Whiskylake. By substituting the formula, we can calculate the decimal value of our GPIO Pin:
The hexadecimal value of our APICPIN is 6d, and its decimal value is 109, which satisfies the following condition of the formula:
ElseIf APICPIN > 95 And APICPIN <= 119 Then
GPIOPIN = APICPIN // GPIOPIN = 109
6d
.
At the same time, we will find that our APICPIN also satisfies the following condition:
ElseIf APICPIN > 108 And APICPIN <= 115 Then
GPIOPIN2 = APICPIN - 44 // GPIOPIN2 = 109 - 44 = 65
41
.
We can see that we have calculated two values, 6d
and 41
, and we can try to verify them one by one.
In very rare cases, the calculated GPIO Pin value doesn't work. In this case, you can try some common values:
0x17
,0x17B
,0x34
and0x55
.
Finally, I tried the GPIO Pin value of my GDX1515 touchpad as 6d
:
Here is a picture for a more intuitive view.
Final Effect¶
Using IORegistryExplorer, we can see that our GDX1515 niche touchpad finally works in GPIO interrupt mode. We can see the gpioPin
and gpioIRQ
attribute values. It's perfect:
Let's also look at the dmesg log situation:
# Found the GDX1515 touchpad of the I2C protocol
[ 44.313108]: VoodooI2CControllerDriver::pci8086,2e9 Found I2C device: GDX1515
# ETPD found a valid _CRS method
[ 44.313178]: VoodooI2CDeviceNub::ETPD Found valid resources from _CRS method
[ 44.313182]: VoodooI2CControllerDriver::pci8086,2e8 Got bus configuration values
[ 44.313231]: VoodooI2CDeviceNub::ETPD Returned index 0x0 from _DSM or XDSM method is not supported
[ 44.313235]: VoodooI2CDeviceNub::ETPD Could not retrieve resources from _DSM or XDSM method
# ETPD found a valid GPIO interrupt
[ 44.313244]: VoodooI2CDeviceNub::ETPD Found valid GPIO interrupts
[ 44.313344]: VoodooI2CControllerDriver::pci8086,2e8 Publishing device nubs
# ETPD got the GPIO controller
[ 44.313347]: VoodooI2CDeviceNub::ETPD Got GPIO Controller! VoodooGPIOCannonLakeLP
[ 44.816012]: VoodooI2CHIDDevice:0x100000738 start
# The reset of the GDX1515 device startup is completed
[ 44.919729]: VoodooI2CHIDDevice::GDX1515 Device initiated reset accomplished
[ 45.050029]: VoodooI2CHIDDevice:0x100000738 creating interfaces
[ 45.051068]: VoodooI2CHIDDevice:0x100000738 Matching has vendor DeviceUsagePage : ff0c bundleIdentifier com.apple.AppleUserHIDDrivers ioclass AppleUserHIDEventService but transport and vendorID is missing
[ 45.053582]: VoodooI2CPrecisionTouchpadHIDEventDriver:0x10000073d start
[ 45.059693]: open by VoodooI2CPrecisionTouchpadHIDEventDriver 0x10000073d (0x0)
# GDX1515 enters Precision Touchpad Mode (PTP) mode, that is, high - precision touchpad mode
[ 45.059739]: VoodooI2CPrecisionTouchpadHIDEventDriver::GDX1515 Putting device into Precision Touchpad Mode
For comparison, the log situations when it doesn't work in GPIO interrupt mode are attached below:
- Log in polling mode without custom DSDT
[ 48.517816]: VoodooI2CControllerDriver::pci8086,2e9 Found I2C device: GDX1515
[ 48.517869]: VoodooI2CDeviceNub::ETPD Found valid resources from _CRS method
[ 48.517913]: VoodooI2CDeviceNub::ETPD Returned index 0x0 from _DSM or XDSM method is not supported
[ 48.517917]: VoodooI2CDeviceNub::ETPD Could not retrieve resources from _DSM or XDSM method
# ETPD warning, found an incompatible APIC pin value 6d, which is greater than 2f
[ 48.517925]: VoodooI2CDeviceNub::ETPD Warning: Incompatible APIC interrupt pin (0x6d > 0x2f)
# ETPD can't find any APIC or GPIO interrupts. You may run in polling mode.
[ 48.517931]: VoodooI2CDeviceNub::ETPD Warning: Could not find any APIC nor GPIO interrupts. Your chosen satellite will run in polling mode if implemented.
[ 48.519529]: VoodooI2CHIDDevice:0x100000725 start
# GDX1515 can't get the interrupt event source and switches to polling mode
[ 48.519539]: VoodooI2CHIDDevice::GDX1515 Warning: Could not get interrupt event source, using polling instead
[ 48.722472]: VoodooI2CHIDDevice::GDX1515 Device initiated reset accomplished
[ 48.854707]: VoodooI2CHIDDevice:0x100000725 creating interfaces
[ 48.856351]: VoodooI2CHIDDevice:0x100000725 Matching has vendor DeviceUsagePage : ff0c bundleIdentifier com.apple.AppleUserHIDDrivers ioclass AppleUserHIDEventService but transport and vendorID is missing
[ 48.860549]: VoodooI2CPrecisionTouchpadHIDEventDriver:0x10000072a start
[ 48.866671]: open by VoodooI2CPrecisionTouchpadHIDEventDriver 0x10000072a (0x0)
# GDX1515 enters Precision Touchpad Mode (PTP) mode, that is, high - precision touchpad mode
[ 48.866716]: VoodooI2CPrecisionTouchpadHIDEventDriver::GDX1515 Putting device into Precision Touchpad Mode
- Log with custom DSDT but incorrect GPIO
[ 37.835221]: VoodooI2CControllerDriver::pci8086,2e9 Found I2C device: GDX1515
[ 37.835266]: VoodooI2CDeviceNub::ETPD Found valid resources from _CRS method
[ 37.835298]: VoodooI2CDeviceNub::ETPD Returned index 0x0 from _DSM or XDSM method is not supported
[ 37.835306]: VoodooI2CDeviceNub::ETPD Could not retrieve resources from _DSM or XDSM method
[ 37.835309]: KextLog: AuxKC bundle com.alexandred.VoodooI2CServices marked as loadable
# ETPD found valid GPIO interrupts.
[ 37.835312]: VoodooI2CDeviceNub::ETPD Found valid GPIO interrupts
[ 37.835408]: VoodooI2CDeviceNub::ETPD Got GPIO Controller! VoodooGPIOCannonLakeLP
[ 38.337232]: VoodooI2CHIDDevice:0x100000761 start
# Failed to obtain the hardware pin 81 of the GPIO pin (decimal of the wrong GPIO Pin), failed to obtain the interrupt event source, and switched to polling instead.
[ 38.337263]: VoodooGPIOCannonLakeLP::Failed getting hardware pin for GPIO pin 81VoodooI2CHIDDevice::GDX1515 Warning: Could not get interrupt event source, using polling instead
[ 38.539715]: VoodooI2CHIDDevice::GDX1515 Device initiated reset accomplished
[ 38.670485]: VoodooI2CHIDDevice:0x100000761 creating interfaces
[ 38.672136]: VoodooI2CHIDDevice:0x100000761 Matching has vendor DeviceUsagePage : ff0c bundleIdentifier com.apple.AppleUserHIDDrivers ioclass AppleUserHIDEventService but transport and vendorID is missing
[ 38.676469]: VoodooI2CPrecisionTouchpadHIDEventDriver:0x100000766 start
[ 38.682612]: open by VoodooI2CPrecisionTouchpadHIDEventDriver 0x100000766 (0x0)
# GDX1515 enters Precision Touchpad Mode (PTP) mode, that is, high - precision touchpad mode.
[ 38.682668]: VoodooI2CPrecisionTouchpadHIDEventDriver::GDX1515 Putting device into Precision Touchpad Mode